﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

//https://go.microsoft.com/fwlink/?LinkId=234236 上介绍了“用户控件”项模板

namespace Acoris.UWP.Demo
{
    using System.Collections;
    using System.Numerics;
    using System.Text.RegularExpressions;

    using Microsoft.Graphics.Canvas;
    using Microsoft.Graphics.Canvas.Brushes;
    using Microsoft.Graphics.Canvas.Geometry;
    using Microsoft.Graphics.Canvas.Text;

    [Demo(typeof(TreeDiagram), Name = "树形图")]
    public sealed partial class TreeDiagram : UserControl
    {
        private TreeDiagramRenderer renderer;
        private TreeDiagramNode     tree;

        private const string RegexPattern = @"(?'intent'(!|！)+)(?'content'\w+)\r";

        private const string TestTreeDiagramNodeBuilding = "! 一级目录\n!!二级目录\n!!!三级目录\n!!二级目录\n!!二级目录\n!!!三级目录\n";

        //  一级目录
        //      二级目录
        //          三级目录
        //      二级目录
        //      二级目录
        //          三级目录
        //

        private Regex GrammerMatcher = new Regex(RegexPattern);


        public TreeDiagram() {
            this.InitializeComponent();
            this.renderer = new TreeDiagramRenderer(canvas);
        }

        private void RebuildTreeDiagramTree(object sender, TextChangedEventArgs e) {
            tree?.Dispose();
            tree = BuildDiagramNode(input.Text);
            canvas.Invalidate();
        }

        private TreeDiagramNode BuildDiagramNode(string text) {

            var context = new Stack<TreeDiagramNode>();
            var node = (TreeDiagramNode)null;

            var matchResults = GrammerMatcher.Matches(text);
            foreach (Match match in matchResults) {
                var intent = match.Groups["intent"].Value.Length;
                var content = match.Groups["content"].Value;

                if (context.Count == 0) {
                    node = new TreeDiagramNode
                    {
                        Intent = intent,
                        Content = content
                    };
                    context.Push(node);
                }
                else if (context.Peek().Intent < intent) {
                    var newNode = new TreeDiagramNode
                    {
                        Intent = intent,
                        Content = content
                    };
                    node.SubNodes.Add(newNode);
                    node = newNode;
                    context.Push(node);
                }
                else if (context.Peek().Intent > intent) {
                    context.Pop();  // last intent
                    context.Pop();  // same intent
                    var newNode = new TreeDiagramNode
                    {
                        Intent = intent,
                        Content = content
                    };
                    node = context.Peek(); // parent intent
                    node.SubNodes.Add(newNode);
                    node = newNode;
                    context.Push(node);
                }
                else {
                    context.Pop();
                    node = context.Peek();
                    var newNode = new TreeDiagramNode
                    {
                        Intent = intent,
                        Content = content
                    };
                    node.SubNodes.Add(newNode);
                    node = newNode;
                    context.Push(newNode);
                }
            }

            while (context.Count > 0) {
                node = context.Pop();
            }

            return node;
        }

        private void Render(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args) {

            if (tree == null) {
                return;
            }

            renderer.Render(args.DrawingSession, tree);
        }
    }

    /// <summary>
    /// document node
    /// </summary>
    sealed class TreeDiagramNode : IEnumerable<TreeDiagramNode>, IDisposable
    {
        public Thickness Margin { get; set; } = new Thickness(16, 16, 0, 0);

        /// <summary>
        /// 
        /// </summary>
        public string Content { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int Intent { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public CanvasTextLayout TextLayout;

        /// <summary>
        /// 
        /// </summary>
        public readonly List<TreeDiagramNode> SubNodes = new List<TreeDiagramNode>();

        public float SubHeight;

        public float Height;

        public float SubWidth;

        public float Width;

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString() {
            return Content;
        }

        public IEnumerator<TreeDiagramNode> GetEnumerator() {
            return ((IEnumerable<TreeDiagramNode>)this.SubNodes).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return ((IEnumerable<TreeDiagramNode>)this.SubNodes).GetEnumerator();
        }

        public void Dispose() {
            TextLayout?.Dispose();

            foreach (var subNode in SubNodes) {
                subNode.Dispose();
            }
        }
    }

    /// <summary>
    /// 
    /// </summary>
    sealed class TreeDiagramRenderer
    {
        private ICanvasResourceCreator renderer;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="renderer"></param>
        public TreeDiagramRenderer(ICanvasResourceCreator renderer) {
            this.renderer = renderer;
        }

        public void Render(CanvasDrawingSession session, TreeDiagramNode tree) {

            RecusiveSizeComputing(tree);
            RecusiveDrawing(session, tree);
        }

        private void RecusiveSizeComputing(TreeDiagramNode node) {
            if (node.SubNodes.Count == 0 && !string.IsNullOrEmpty(node.Content)) {
                // computing current node
                node.TextLayout = new CanvasTextLayout(renderer,
                                                        node.Content,
                                                        new CanvasTextFormat(),
                                                        100,
                                                        100);
                // computing size

                var size = ResizeTextLayout(node.TextLayout);

                node.TextLayout
                    .RequestedSize = size;

                node.Width = (float)(size.Width + node.Margin.Left + node.Margin.Right);
                node.Height = (float)(size.Height + node.Margin.Top + node.Margin.Bottom);
            }
            else {

                var maxWidth = 0d;
                var height = 0d;

                // computing sub node
                foreach (var subNode in node.SubNodes) {
                    RecusiveSizeComputing(subNode);

                    maxWidth = Math.Max(maxWidth, subNode.TextLayout
                                                         .RequestedSize
                                                         .Width);
                    height += Math.Max(subNode.Height, subNode.SubHeight) + (float)(node.Margin.Top + node.Margin.Bottom);
                }

                // computing current node
                node.TextLayout = new CanvasTextLayout(renderer,
                                                        node.Content,
                                                        new CanvasTextFormat(),
                                                        100,
                                                        100);
                var size = ResizeTextLayout(node.TextLayout);

                node.TextLayout
                    .RequestedSize = size;

                node.Width = (float)(size.Width + node.Margin.Left + node.Margin.Right);
                node.Height = (float)(size.Height + node.Margin.Top + node.Margin.Bottom);

                node.SubHeight = (float)height;
                node.SubWidth = (float)maxWidth;
            }
        }

        private void RecusiveDrawing(CanvasDrawingSession session, TreeDiagramNode node) {
            RecusiveDrawingImpl(session, node, 100, 100);
        }

        private void RecusiveDrawingImpl(CanvasDrawingSession session, TreeDiagramNode node, float x, float y) {

            if (node.SubNodes.Count == 0) {
                session.DrawTextLayout(node.TextLayout,
                                        new Vector2(
                                                    (float)(x + node.Margin.Left),
                                                    (float)(y + node.Margin.Top)
                                                   ),
                                       Colors.Black);
            }
            else {

                if (node.TextLayout != null) {
                    session.DrawTextLayout(node.TextLayout, GetCenterPoint(x + (float)node.Margin.Left, y, node.SubHeight), Colors.Black);
                    x += (float)(node.TextLayout.RequestedSize.Width + node.Margin.Right);
                }

                x += (float)node.Margin.Left; // glyph margin -left

                DrawingGlyph(session, x + (float)node.Margin.Left, y + (float)node.Margin.Top, node.SubHeight);

                x += (float)(node.Margin.Left + node.Margin.Right + ComputingGlyphSize().Width);

                foreach (var subNode in node.SubNodes) {
                    RecusiveDrawingImpl(session, subNode, x, y);
                    y += Math.Max(subNode.Height, subNode.SubHeight) + (float)(node.Margin.Top + node.Margin.Bottom);
                }
            }

        }

        private Vector2 GetCenterPoint(float x, float y, float height) {
            return new Vector2(x, y + (height / 2f));
        }

        private Size ComputingGlyphSize() {
            return new Size(16, 0);
        }

        private void DrawingGlyph(CanvasDrawingSession session, float x, float y, float height) {
            session.DrawLine(new Vector2(x + 8, y),             new Vector2(x + 16, y),                 Colors.Black,1);
            session.DrawLine(new Vector2(x + 8, y),             new Vector2(x + 8, y + height),        Colors.Black,1);
            session.DrawLine(new Vector2(x , y + height / 2f),  new Vector2(x + 8, y + height / 2f),    Colors.Black,1);
            session.DrawLine(new Vector2(x + 8, y + height),    new Vector2(x + 16, y + height),        Colors.Black,1);
        }

        private Size ResizeTextLayout(CanvasTextLayout layout) {
            var width = 0f;
            var height = 0f;
            foreach (var cluster in layout.ClusterMetrics) {
                width += cluster.Width;
            }

            foreach (var line in layout.LineMetrics) {
                height += line.Height;
            }

            return new Size(width, height);
        }
    }

}
